package com.linkgie.galaxyframework.utils;

/**
 * 命名格式；
 */
public class NamingUtils {

	public static final char SNAKE_SEPERATOR = '_';

	public static final char KEBAB_SEPERATOR = '-';

	/**
	 * 将变量名转为标准的蛇形命名法(SnakeCase)格式;
	 * <p>
	 * 
	 * 方法会去除两端空格后开始转换，转换过程如下：
	 * <p>
	 * 1. 所有中间的空格转为 _ ；<br>
	 * 2. 所有大写字母转为小写，并且在该字母当前位置插入 _ ，该字母后移 1 位；<br>
	 * 3. 所有连续多个 _ 转为一个 _ ；<br>
	 * 
	 * <p>
	 * 
	 * 例如：<br>
	 * 1. 输入： DataObject ，输出： data_object ； <br>
	 * 2. 输入： dataObject ，输出： data_object ； <br>
	 * 3. 输入： Data Object ，输出： data_object ；<br>
	 * 4. 输入： data__object ，输出： data_object ；<br>
	 * 5. 输入： __data__object__ ，输出： _data_object_ ； <br>
	 * 6. 输入： $dataObject ，输出： $data_object ；
	 * 7. 输入： data-object ，输出： data_object ；<br>
	 * 8. 输入： Data-Object ，输出： data_object ；<br>
	 * <p>
	 * 
	 * @param name 变量名
	 * @return
	 */
	public static String toSnakeCase(String name) {
		if (name == null) {
			return null;
		}
		name = name.trim();
		final int length = name.length();
		if (length == 0) {
			return "";
		}

		StringBuilder builder = new StringBuilder();
		char ch;
		boolean underlineLeading = false;// 是否前一个字符是下划线 _ ;
		for (int i = 0; i < length; i++) {
			ch = name.charAt(i);
			if (ch >= 'A' && ch <= 'Z') {
				// 大写转小写，ASCII码增加 32 ；
				ch = (char) (ch + 32);

				if (!underlineLeading && i > 0) {
					// 前导字符不是下划线，且当前位置不是首字母时，加入分隔符；
					builder.append(SNAKE_SEPERATOR);
				}
				builder.append(ch);
				underlineLeading = false;
			} else if (ch == ' ' || ch == KEBAB_SEPERATOR || ch == SNAKE_SEPERATOR) {
				if (!underlineLeading) {
					builder.append(SNAKE_SEPERATOR);
					underlineLeading = true;
				}
			} else {
				// 既不是大写字母，也不是空格和下划线，则保留原字符；
				builder.append(ch);
				underlineLeading = false;
			}
		}

		return builder.toString();

		// 基于 GUAVA 的实现存在缺陷，不能满足本函数的算法设计要求；
		// return null == name ? name :
		// CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, name);
	}

	/**
	 * 将变量名转为标准的蛇形命名法(SnakeCase)格式;
	 * <p>
	 * 
	 * 方法会去除两端空格后开始转换，转换过程如下：
	 * <p>
	 * 1. 所有中间的空格转为 - ；<br>
	 * 2. 所有大写字母转为小写，并且在该字母当前位置插入 - ，该字母后移 1 位；<br>
	 * 3. 所有连续多个 - 转为一个 - ；<br>
	 * 
	 * <p>
	 * 
	 * 例如：<br>
	 * 1. 输入： DataObject ，输出： data-object ； <br>
	 * 2. 输入： dataObject ，输出： data-object ； <br>
	 * 3. 输入： Data Object ，输出： data-object ；<br>
	 * 4. 输入： data--object ，输出： data-object ；<br>
	 * 5. 输入： --data--object-- ，输出： data-object ； <br>
	 * 6. 输入： $dataObject ，输出： $data-object ；
	 * <p>
	 * 
	 * @param name 变量名
	 * @return
	 */
	public static String toKebabCase(String name) {
		if (name == null) {
			return null;
		}
		name = name.trim();
		final int length = name.length();
		if (length == 0) {
			return "";
		}

		StringBuilder builder = new StringBuilder();
		char ch;
		boolean kebabLeading = false;// 是否前一个字符是短线 - ;
		for (int i = 0; i < length; i++) {
			ch = name.charAt(i);
			if (ch >= 'A' && ch <= 'Z') {
				// 大写转小写，ASCII码增加 32 ；
				ch = (char) (ch + 32);

				if (!kebabLeading && i > 0) {
					// 前导字符不是短线，且当前位置不是首字母时，加入分隔符；
					builder.append(KEBAB_SEPERATOR);
				}
				builder.append(ch);
				kebabLeading = false;
			} else if (ch == ' ') {
				if (!kebabLeading) {
					builder.append(KEBAB_SEPERATOR);
					kebabLeading = true;
				}
			} else if (ch == KEBAB_SEPERATOR) {
				if (!kebabLeading) {
					builder.append(KEBAB_SEPERATOR);
					kebabLeading = true;
				}
			} else {
				// 既不是大写字母，也不是空格和短线，则保留原字符；
				builder.append(ch);
				kebabLeading = false;
			}
		}
		// 去掉前后连续的短线；
		if (builder.length() > 0) {
			int i = 0;
			while (builder.charAt(i) == KEBAB_SEPERATOR) {
				i++;
			}
			int j = builder.length() - 1;
			while (j > i && builder.charAt(j) == KEBAB_SEPERATOR) {
				j--;
			}
			return builder.substring(i, j + 1);
		}

		return "";

		// 基于 GUAVA 的实现存在缺陷，不能满足本函数的算法设计要求；
		// return null == name ? name :
		// CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, name);
	}

	/**
	 * 将变量名转为驼峰命名法(CamelCase)格式;
	 * 
	 * 方法会去除两端空格后开始转换，转换过程如下：
	 * <p>
	 * 1. 去掉所有中间的空格；<br>
	 * 2. 去掉所有的短线 - 或下划线 _ ，并将一个或连续多个短线、下划线之后的首个字母转为大写；<br>
	 * 
	 * <p>
	 * 
	 * 例如：<br>
	 * 1. 输入： DataObject ，输出： DataObject ； <br>
	 * 2. 输入： dataObject ，输出： dataObject ； <br>
	 * 3. 输入： $dataObject ，输出： $dataObject ； <br>
	 * 4. 输入： Data Object ，输出： DataObject ；<br>
	 * 5. 输入： data_object ，输出： dataObject ；<br>
	 * 6. 输入： _data_object ，输出： DataObject ；<br>
	 * 7. 输入： __data__object__ ，输出： DataObject ；<br>
	 * 8. 输入： data-object ，输出： dataObject ；<br>
	 * 9. 输入： --data-object-- ，输出： DataObject ； <br>
	 * 
	 * @param name           变量名
	 * @param upperCamelCase 是否采用“大驼峰式”，即首字母大写的驼峰格式；
	 * @return
	 */
	public static String toCamelCase(String name) {
		return toCamelCase(name, CamelCaseStyle.ORGIN);
	}

	/**
	 * 将变量名转为驼峰命名法(CamelCase)格式;
	 * 
	 * 方法会去除两端空格后开始转换，转换过程如下：
	 * <p>
	 * 1. 去掉所有中间的空格；<br>
	 * 2. 去掉所有的短线 - 或下划线 _ ，并将一个或连续多个短线、下划线之后的首个字母转为大写；<br>
	 * 
	 * <p>
	 * 
	 * 例如：<br>
	 * 1. 输入： DataObject ，输出： DataObject ； <br>
	 * 2. 输入： dataObject ，输出： dataObject ； <br>
	 * 3. 输入： $dataObject ，输出： $dataObject ； <br>
	 * 4. 输入： Data Object ，输出： DataObject ；<br>
	 * 5. 输入： data_object ，输出： dataObject ；<br>
	 * 6. 输入： _data_object ，输出： DataObject ；<br>
	 * 7. 输入： __data__object__ ，输出： DataObject ；<br>
	 * 8. 输入： data-object ，输出： dataObject ；<br>
	 * 9. 输入： --data-object-- ，输出： DataObject ； <br>
	 * 
	 * @param name           变量名
	 * @param upperCamelCase 是否采用“大驼峰式”，即首字母大写的驼峰格式；
	 * @return
	 */
	public static String toCamelCase(String name, CamelCaseStyle style) {
		if (name == null) {
			return null;
		}
		name = name.trim();
		final int length = name.length();
		if (length == 0) {
			return "";
		}

		StringBuilder builder = new StringBuilder();
		char ch;
		boolean encounterHeadChar = false; // 是否遇到了除短线、下划线和空格之外的首个字符；
		boolean keyLeading = false;// 是否前一个字符是短线 - 或下划线 _ ;
		for (int i = 0; i < length; i++) {
			ch = name.charAt(i);

			if (ch >= 'a' && ch <= 'z') {
				// 当前字符是小写字母;
				if (keyLeading && !(CamelCaseStyle.LOWER == style && !encounterHeadChar)) {
					// 如果前导字符是短线或下划线，且未指定强制用“小驼峰式”，则把小写转为大写；
					ch = (char) (ch - 32);
				} else if (CamelCaseStyle.UPPER == style && !encounterHeadChar) {
					// 采用大驼峰式，首字母大写；
					ch = (char) (ch - 32);
				}
				builder.append(ch);
				keyLeading = false;
				encounterHeadChar = true;
			} else if (ch >= 'A' && ch <= 'Z') {
				if (CamelCaseStyle.LOWER == style && !encounterHeadChar) {
					// 采用小驼峰式，首字母小写；
					ch = (char) (ch + 32);
				}
				builder.append(ch);
				keyLeading = false;
				encounterHeadChar = true;
			} else if (ch == '-' || ch == '_') {
				// 当前字符是短线或者下划线；
				keyLeading = true;
			} else if (ch == ' ') {
				// 忽略掉空格；
				keyLeading = false;
			} else {
				// 其它字符照常输出，并且忽略掉首字母大小写转换的处理；
				builder.append(ch);
				keyLeading = false;
				encounterHeadChar = true;
			}
		}

		return builder.toString();
	}

	/**
	 * 驼峰样式；
	 */
	public static enum CamelCaseStyle {

		/**
		 * 大驼峰式，即首字母大写；
		 */
		UPPER,

		/**
		 * 小驼峰式，即首字母小写；
		 */
		LOWER,

		/**
		 * 保留原值，无论其大小写；
		 */
		ORGIN
	}

}
